package me.stone.stanimeclient.page.home

import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import dagger.hilt.android.lifecycle.HiltViewModel
import kotlinx.coroutines.ExperimentalCoroutinesApi
import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.channels.Channel
import kotlinx.coroutines.flow.Flow
import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.SharedFlow
import kotlinx.coroutines.flow.SharingStarted
import kotlinx.coroutines.flow.StateFlow
import kotlinx.coroutines.flow.filter
import kotlinx.coroutines.flow.filterNotNull
import kotlinx.coroutines.flow.map
import kotlinx.coroutines.flow.mapLatest
import kotlinx.coroutines.flow.onEach
import kotlinx.coroutines.flow.receiveAsFlow
import kotlinx.coroutines.flow.shareIn
import kotlinx.coroutines.flow.stateIn
import kotlinx.coroutines.launch
import me.stone.stanimeclient.R
import me.stone.stanimeclient.core.common.extensions.throttle
import me.stone.stanimeclient.core.common.extensions.tryOffer
import me.stone.stanimeclient.core.common.utils.WhileViewSubscribed
import me.stone.stanimeclient.core.data.domain.fan.LoadBannerCase
import me.stone.stanimeclient.core.data.domain.fan.LoadDailyDataCase
import me.stone.stanimeclient.core.data.domain.fan.LoadHomeReListCase
import me.stone.stanimeclient.core.data.domain.Result
import me.stone.stanimeclient.core.model.FanDailyUpdateItem
import me.stone.stanimeclient.core.model.FanSimpleItem
import timber.log.Timber
import javax.inject.Inject
import kotlin.random.Random

@HiltViewModel
class HomeViewModel @Inject constructor(
    loadBannerCase: LoadBannerCase,
    loadDailyDataCase: LoadDailyDataCase,
    loadHomeReListCase: LoadHomeReListCase
) : ViewModel() {

    private val _errorMessage = Channel<String>(1, BufferOverflow.DROP_OLDEST)

    val errorMessage: Flow<String> =
        _errorMessage.receiveAsFlow().shareIn(viewModelScope, WhileViewSubscribed)

    private val loadBannerResult: StateFlow<Result<List<FanSimpleItem>>> =
        loadBannerCase(Unit)
            .onEach {
                if (it is Result.Error) {
                    _errorMessage.tryOffer(it.error.message ?: "Error")
                }
            }
            .stateIn(viewModelScope, WhileViewSubscribed, Result.Success(emptyList()))

    @OptIn(ExperimentalCoroutinesApi::class)
    val bannerData: StateFlow<List<FanSimpleItem>> =
        loadBannerResult
            .filter { it is Result.Success }
            .mapLatest { it.dataOrNull ?: emptyList() }
            .stateIn(viewModelScope, WhileViewSubscribed, emptyList())

    private val loadDailyDataResult: StateFlow<Result<Map<Int, List<FanDailyUpdateItem>>>> =
        loadDailyDataCase(Unit)
            .onEach {
                if (it is Result.Error) {
                    _errorMessage.tryOffer(it.error.message ?: "Error")
                }
            }
            .stateIn(viewModelScope, WhileViewSubscribed, Result.Success(emptyMap()))

    @OptIn(ExperimentalCoroutinesApi::class)
    val dailyData: StateFlow<Map<Int, List<FanDailyUpdateItem>>> =
        loadDailyDataResult
            .filter { it is Result.Success }
            .mapLatest { it.dataOrNull ?: emptyMap() }
            .stateIn(viewModelScope, WhileViewSubscribed, emptyMap())

    private val _recommendQueryList =
        loadHomeReListCase(Unit)
            .stateIn(viewModelScope, SharingStarted.Eagerly, Result.Loading)

    val recommendQuery: SharedFlow<String> = _recommendQueryList
        .filter { it is Result.Success }
        .map { it.dataOrNull?.randomOne() ?: "" }
        .shareIn(viewModelScope, SharingStarted.Lazily)

    private fun <T> List<T>.randomOne(): T? {
        if (isEmpty()) return null
        val randomIndex = Random.nextInt(size)
        return this[randomIndex]
    }

    private val _navigation = MutableSharedFlow<Int?>()

    val navigationId: Flow<Int> = _navigation
        .filterNotNull()
        .throttle(500L)
        .shareIn(viewModelScope, SharingStarted.Eagerly)

    private fun navigationWithId(id: Int) {
        viewModelScope.launch {
            _navigation.emit(id)
        }
    }

    fun toRecommendFragment() {
        navigationWithId(R.id.to_recommend)
    }

    fun toLastUpdateFragment() {
        navigationWithId(R.id.to_updated)
    }

    fun toRankFragment() {
        navigationWithId(R.id.to_rank)
    }


}